home *** CD-ROM | disk | FTP | other *** search
- // Asuka - VirtualDub Build/Post-Mortem Utility
- // Copyright (C) 2005-2006 Avery Lee
- //
- // This program is free software; you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation; either version 2 of the License, or
- // (at your option) any later version.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with this program; if not, write to the Free Software
- // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- #include "stdafx.h"
- #include <stdio.h>
- #include <exception>
- #include <hash_map>
- #include <vd2/system/error.h>
- #include <vd2/system/filesys.h>
- #include <vd2/system/vdalloc.h>
-
- #include "utils.h"
- #include "glc.h"
-
- using namespace GLCIL;
-
- namespace {
- enum {
- kTokenIdent = 256,
- kTokenInteger,
- kTokenDouble,
- kTokenForeignCode,
- kTokenCount
- };
-
- }
-
- IGLCFragmentShader *CompileFragmentShaderNVRegisterCombiners(GLCErrorSink& errout, const GLCFragmentShader& shader, bool NV_register_combiners_2);
- IGLCFragmentShader *CompileFragmentShaderATIFragmentShader(GLCErrorSink& errout, const GLCFragmentShader& shader);
-
- ///////////////////////////////////////////////////////////////////////////////
- struct GLCKeyword {
- public:
- GLCKeyword(const char *s) : mpKeyword(s), mKeywordLen(strlen(s)) {}
-
- const char *mpKeyword;
- int mKeywordLen;
- };
-
- ///////////////////////////////////////////////////////////////////////////////
-
- class GLCTokenizer : public GLCErrorSink {
- public:
- GLCTokenizer();
- ~GLCTokenizer();
-
- void Init(const char *srcName, const char *src, uint32 len);
- int Token();
- void Push(int tok) { mPushedToken = tok; }
- void EnableNewlines(bool enable) { mbReturnNewlines = enable; }
- void EnableForeignCode(bool enable) { mbReturnForeignCode = enable; }
-
- GLCCodeLocation GetLocation() const {
- GLCCodeLocation loc = { mpSourceName, mLine, mpSrc - mpLineStart };
- return loc;
- }
-
- int GetInteger() const { return mTokenInt; }
- double GetDouble() const { return mTokenDbl; }
- const char *GetToken() const { return mpTokenStart; }
- int GetTokenLen() const { return mTokenLen; }
- int GetLineNumber() const { return mLine; }
-
- bool IsKeyword(const GLCKeyword& keyword) const;
-
- protected:
- bool mbReturnNewlines;
- bool mbReturnForeignCode;
- const char *mpSrc;
- const char *mpSrcEnd;
- const char *mpLineStart;
- const char *mpSourceName;
- const char *mpTokenStart;
- int mTokenLen;
- int mTokenInt;
- int mPushedToken;
- int mLine;
- double mTokenDbl;
- };
-
- GLCTokenizer::GLCTokenizer() {
- }
-
- GLCTokenizer::~GLCTokenizer() {
- }
-
- void GLCTokenizer::Init(const char *srcName, const char *src, uint32 len) {
- mpSrc = src;
- mpSrcEnd = src + len;
- mpLineStart = src;
- mpSourceName = srcName;
- mLine = 1;
- mPushedToken = 0;
- mbReturnNewlines = false;
- mbReturnForeignCode = false;
- }
-
- int GLCTokenizer::Token() {
- if (mPushedToken) {
- int tok = mPushedToken;
- mPushedToken = 0;
- return tok;
- }
-
- // skip whitespace and comments
- char c;
- for(;;) {
- if (mpSrc == mpSrcEnd)
- return 0;
-
- c = *mpSrc++;
-
- // skip whitespace
- if (c == ' ' || c == '\t')
- continue;
-
- // skip newlines
- if (c == '\n' || c == '\r') {
- ++mLine;
-
- if (mpSrc != mpSrcEnd && *mpSrc == (c ^ ('\n' ^ '\r')))
- ++mpSrc;
-
- mpTokenStart = mpSrc;
- mTokenLen = 0;
- mpLineStart = mpSrc;
- if (mbReturnNewlines)
- return '\n';
- else
- continue;
- }
-
- // skip C++ style comments
- if (c == '/' && mpSrc != mpSrcEnd && mpSrc[0] == '/') {
- ++mpSrc;
-
- while(mpSrc != mpSrcEnd && *mpSrc != '\r' && *mpSrc != '\n')
- ++mpSrc;
-
- continue;
- }
-
- break;
- }
-
- mpTokenStart = mpSrc - 1;
-
- if (mbReturnForeignCode) {
- int braceCount = 0;
-
- for(;;) {
- if (mpSrc == mpSrcEnd)
- break;
-
- c = *mpSrc++;
-
- // skip whitespace
- if (c == ' ' || c == '\t')
- continue;
-
- // skip newlines
- if (c == '\n' || c == '\r') {
- ++mLine;
-
- if (mpSrc != mpSrcEnd && *mpSrc == (c ^ ('\n' ^ '\r')))
- ++mpSrc;
-
- mpLineStart = mpSrc;
- continue;
- }
-
- // skip C++ style comments
- if (c == '/' && mpSrc != mpSrcEnd && mpSrc[0] == '/') {
- ++mpSrc;
-
- while(mpSrc != mpSrcEnd && *mpSrc != '\r' && *mpSrc != '\n')
- ++mpSrc;
-
- continue;
- }
-
- if (c == '{')
- ++braceCount;
- else if (c == '}') {
- --braceCount;
- if (braceCount < 0) {
- --mpSrc;
- break;
- }
- }
- }
-
- mTokenLen = mpSrc - mpTokenStart;
- return kTokenForeignCode;
- }
-
- // check for integers
- if (isdigit((unsigned char)c)) {
- mTokenInt = c - '0';
-
- if (mpSrc != mpSrcEnd) {
- do {
- c = *mpSrc;
- if (!isdigit((unsigned char)c))
- break;
-
- mTokenInt = (mTokenInt * 10) + (c - '0');
- } while(++mpSrc != mpSrcEnd);
-
- if (c == '.') {
- ++mpSrc;
-
- // hmm, it's a double.
- while(mpSrc != mpSrcEnd && isdigit((unsigned char)*mpSrc))
- ++mpSrc;
-
- if (mpSrc != mpSrcEnd) {
- c = *mpSrc;
-
- if (c == 'e' || c == 'E') {
- ++mpSrc;
- if (mpSrc == mpSrcEnd)
- ThrowError("Invalid floating-point constant");
-
- c = *mpSrc;
- if (c == '+' || c == '-')
- ++mpSrc;
-
- if (mpSrc == mpSrcEnd || !isdigit((unsigned char)*mpSrc))
- ThrowError("Invalid floating-point constant");
-
- do {
- ++mpSrc;
- } while(mpSrc != mpSrcEnd && isdigit((unsigned char)*mpSrc));
- }
-
- if (mpSrc != mpSrcEnd && (*mpSrc == 'f' || *mpSrc == 'F'))
- ++mpSrc;
- }
-
- mTokenLen = mpSrc - mpTokenStart;
-
- std::string tmp(mpTokenStart, mpSrc);
- mTokenDbl = strtod(tmp.c_str(), NULL);
-
- return kTokenDouble;
- }
- }
-
- mTokenLen = mpSrc - mpTokenStart;
- return kTokenInteger;
- }
-
- // check for identifiers
- if (c == '_' || isalnum((unsigned char)c)) {
- while(mpSrc != mpSrcEnd) {
- c = *mpSrc;
-
- if (c != '_' && !isalnum((unsigned char)c))
- break;
-
- ++mpSrc;
- }
-
- mTokenLen = mpSrc - mpTokenStart;
- return kTokenIdent;
- }
-
- // check for single char
- if (strchr("{}(),.=;+-*/", c)) {
- mTokenLen = 1;
- return (unsigned char)c;
- }
-
- if (isprint((unsigned char)c))
- ThrowError("Unrecognized character '%c'", c);
- else
- ThrowError("Unrecognized character '0x%02x'", (unsigned char)c);
- }
-
- bool GLCTokenizer::IsKeyword(const GLCKeyword& keyword) const {
- return (mTokenLen == keyword.mKeywordLen && !memcmp(mpTokenStart, keyword.mpKeyword, mTokenLen));
- }
-
- void GLCErrorSink::ThrowError(const char *format, ...) {
- char buf[4096];
- va_list val;
-
- va_start(val, format);
- int cnt = _vsnprintf(buf, 4096, format, val);
- va_end(val);
-
- if ((unsigned)cnt >= 4096)
- buf[cnt] = 0;
-
- const GLCCodeLocation loc(GetLocation());
- throw MyError("Shader compilation failed.\n%s(%d,%d): Error! %s", loc.mpFileName, loc.mLine, loc.mColumn, buf);
- }
-
- void GLCErrorSink::ThrowError(const GLCCodeLocation& loc, const char *format, ...) {
- char buf[4096];
- va_list val;
-
- va_start(val, format);
- int cnt = _vsnprintf(buf, 4096, format, val);
- va_end(val);
-
- if ((unsigned)cnt >= 4096)
- buf[cnt] = 0;
-
- throw MyError("Shader compilation failed.\n%s(%d,%d): Error! %s", loc.mpFileName, loc.mLine, loc.mColumn, buf);
- }
-
- ///////////////////////////////////////////////////////////////////////////////
- class GLCCompiler {
- public:
- GLCCompiler();
- ~GLCCompiler();
-
- void Compile(const char *sourceName, const char *src, uint32 len, FILE *f);
-
- protected:
- void ParseTechnique();
- void ParseFragmentShader(GLCFragmentShader& shader);
- GLCDestArg ParseFragmentShaderDestination();
- GLCSourceArg ParseFragmentShaderArgument();
-
- double ParseConstantDoubleExpression();
-
- void Expect(int c);
- VDNORETURN void Huh(const char *expected, int found);
- std::string TokenName(int tok);
-
- GLCTokenizer mLexer;
- FILE *mpOutput;
-
- vdfastvector<IGLCFragmentShader *> mFragmentShaders;
-
- struct Technique {
- IGLCFragmentShader *mpFragmentShader;
- };
-
- typedef stdext::hash_map<std::string, Technique> Techniques;
- Techniques mTechniques;
- };
-
- GLCCompiler::GLCCompiler() {
- }
-
- GLCCompiler::~GLCCompiler() {
- while(!mFragmentShaders.empty()) {
- delete mFragmentShaders.back();
- mFragmentShaders.pop_back();
- }
- }
-
- namespace {
- static const GLCKeyword sKeywordPass("pass");
- static const GLCKeyword sKeywordTechnique("technique");
- static const GLCKeyword sKeywordFragmentShader("fragmentshader");
- static const GLCKeyword sKeywordFragment_Shader("fragment_shader");
- static const GLCKeyword sKeywordNV_Register_Combiners("NV_register_combiners");
- static const GLCKeyword sKeywordNV_Register_Combiners2("NV_register_combiners2");
- static const GLCKeyword sKeywordATI_Fragment_Shader("ATI_fragment_shader");
- static const GLCKeyword sKeywordARB_Fragment_Shader("ARB_fragment_shader");
- }
-
- void GLCCompiler::Compile(const char *sourceName, const char *src, uint32 len, FILE *f) {
- mpOutput = f;
- mLexer.Init(sourceName, src, len);
-
- while(int tok = mLexer.Token()) {
- if (tok == kTokenIdent) {
- if (mLexer.IsKeyword(sKeywordTechnique)) {
- ParseTechnique();
- } else if (mLexer.IsKeyword(sKeywordFragment_Shader)) {
- GLCFragmentShader shader;
- ParseFragmentShader(shader);
- continue;
- }
- }
- }
-
- // write out fragment shaders
- fputs("/////////////////////////////////////////////////////////////////////////////\n", f);
- fputs("// fragment shaders\n", f);
- fputs("//\n", f);
- const int fragmentShaderCount = mFragmentShaders.size();
-
- for(int i=0; i<fragmentShaderCount; ++i) {
- char buf[64];
- sprintf(buf, "g_fragmentShader%d", i);
-
- mFragmentShaders[i]->Write(mpOutput, buf);
- }
-
- // write out techniques
- fputs("\n", f);
- fputs("/////////////////////////////////////////////////////////////////////////////\n", f);
- fputs("// techniques\n", f);
- fputs("//\n", f);
- fprintf(f, "static const struct VDOpenGLTechnique g_techniques[]={\n");
- Techniques::const_iterator it(mTechniques.begin()), itEnd(mTechniques.end());
- for(; it!=itEnd; ++it) {
- const Technique& tech = it->second;
-
- int index = std::find(mFragmentShaders.begin(), mFragmentShaders.end(), tech.mpFragmentShader) - mFragmentShaders.begin();
-
- fprintf(f, "\t{ &g_fragmentShader%d, %s },\n", index, tech.mpFragmentShader->GetTypeString());
- }
- fprintf(f, "};\n");
-
- it = mTechniques.begin();
- for(int techIndex = 0; it!=itEnd; ++it, ++techIndex) {
- const char *name = it->first.c_str();
-
- fprintf(f, "static const int kVDOpenGLTechIndex_%s = %d;\n", name, techIndex);
- }
-
-
- printf("Asuka: %d techniques, %d fragment shaders.\n", mTechniques.size(), mFragmentShaders.size());
- }
-
- namespace {
- class ARBFragmentShader : public vdrefcounted<IGLCFragmentShader> {
- public:
- ARBFragmentShader(const char *s, int len) {
- mText.assign(s, s+len);
- printf("%.*s\n", len, s);
- }
-
- const char *GetTypeString() {
- return "kVDOpenGLFragmentShaderModeARBFS";
- }
-
- void Write(FILE *f, const char *sym) {
- fprintf(f, "static const char %s[]=\n", sym);
-
- bool open = false;
- bool sol = true;
- int len = mText.size();
- const char *s = mText.data();
-
- for(int i=0; i<len; ++i) {
- char c = s[i];
-
- if (!open) {
- putc('"', f);
- open = true;
- }
-
- if (c == '\r')
- continue;
-
- if (c == '\t')
- c = ' ';
-
- if (c == ' ' && sol)
- continue;
-
- if (c == '\n' || c == '\\' || c == '"')
- putc('\\', f);
-
- if (c == '\n') {
- fputs("n\"\n", f);
- open = false;
- sol = true;
- } else {
- sol = false;
- putc(c, f);
- }
- }
-
- if (open)
- fputs("\\n\"\n", f);
-
- fprintf(f, ";\n");
- }
-
- public:
- vdfastvector<char> mText;
- };
- }
-
- void GLCCompiler::ParseTechnique() {
- std::string namestr;
-
- Expect(kTokenIdent);
- const char *name = mLexer.GetToken();
- namestr.assign(name, name + mLexer.GetTokenLen());
-
- std::pair<Techniques::iterator, bool> result(mTechniques.insert(Techniques::value_type(namestr, Technique())));
-
- if (!result.second)
- mLexer.ThrowError("Technique '%s' already defined", namestr.c_str());
-
- Technique& tech = result.first->second;
-
- Expect('{');
-
- Expect(kTokenIdent);
- if (!mLexer.IsKeyword(sKeywordPass))
- Huh("pass declaration", kTokenIdent);
-
- Expect('{');
-
- for(;;) {
- int tok = mLexer.Token();
-
- if (tok == '\n')
- continue;
- else if (tok == '}')
- break;
- else if (tok == kTokenIdent) {
- if (mLexer.IsKeyword(sKeywordFragmentShader)) {
- Expect('=');
- Expect(kTokenIdent);
- if (mLexer.IsKeyword(sKeywordFragment_Shader)) {
- tok = mLexer.Token();
-
- if (tok != kTokenIdent)
- Huh("fragment shader profile", tok);
-
- enum {
- kProfileNVRC,
- kProfileNVRC2,
- kProfileATIFS,
- kProfileARBFS
- } profile;
-
- if (mLexer.IsKeyword(sKeywordNV_Register_Combiners))
- profile = kProfileNVRC;
- else if (mLexer.IsKeyword(sKeywordNV_Register_Combiners2))
- profile = kProfileNVRC2;
- else if (mLexer.IsKeyword(sKeywordATI_Fragment_Shader))
- profile = kProfileATIFS;
- else if (mLexer.IsKeyword(sKeywordARB_Fragment_Shader))
- profile = kProfileARBFS;
- else
- mLexer.ThrowError("Unknown shader profile '%.*s'", mLexer.GetTokenLen(), mLexer.GetToken());
-
- vdautoptr<IGLCFragmentShader> fshader;
- if (profile == kProfileARBFS) {
- Expect('{');
- mLexer.EnableForeignCode(true);
- Expect(kTokenForeignCode);
- mLexer.EnableForeignCode(false);
- fshader = new ARBFragmentShader(mLexer.GetToken(), mLexer.GetTokenLen());
- Expect('}');
- } else {
- GLCFragmentShader shader;
- ParseFragmentShader(shader);
-
- switch(profile) {
- case kProfileNVRC:
- fshader = CompileFragmentShaderNVRegisterCombiners(mLexer, shader, false);
- break;
- case kProfileNVRC2:
- fshader = CompileFragmentShaderNVRegisterCombiners(mLexer, shader, true);
- break;
- case kProfileATIFS:
- fshader = CompileFragmentShaderATIFragmentShader(mLexer, shader);
- break;
- }
- }
-
- tech.mpFragmentShader = fshader;
-
- mFragmentShaders.push_back(fshader.release());
- }
- Expect(';');
- continue;
- }
- }
-
- Huh("technique parameter", tok);
- }
-
- Expect('}');
- }
-
- void GLCCompiler::ParseFragmentShader(GLCFragmentShader& shader) {
- shader.mLocation = mLexer.GetLocation();
-
- Expect('{');
-
- mLexer.EnableNewlines(true);
-
- bool coissue = false;
- for(;;) {
- int tok = mLexer.Token();
-
- if (tok == '+') {
- coissue = true;
- continue;
- }
-
- if (tok == '}')
- break;
- else if (tok == '\n') {
- if (coissue)
- mLexer.ThrowError("Syntax error");
- continue;
- } else if (tok == kTokenIdent) {
- const GLCCodeLocation loc = mLexer.GetLocation();
- const char *s = mLexer.GetToken();
- int toklen = mLexer.GetTokenLen();
- int insnlen = 0;
-
- // parse instruction itself
- while(insnlen < toklen && s[insnlen] != '_')
- ++insnlen;
-
- int insn = 0;
- int insn_dests = 1;
- int insn_sources = 0;
- switch(insnlen) {
- case 2:
- if (!memcmp(s, "dd", 2)) {
- insn = kFSOpDd;
- insn_dests = 2;
- insn_sources = 4;
- }
- if (!memcmp(s, "dm", 2)) {
- insn = kFSOpDm;
- insn_dests = 2;
- insn_sources = 4;
- }
- break;
- case 3:
- if (!memcmp(s, "mov", 3)) {
- insn = kFSOpMov;
- insn_sources = 1;
- } else if (!memcmp(s, "add", 3)) {
- insn = kFSOpAdd;
- insn_sources = 2;
- } else if (!memcmp(s, "sub", 3)) {
- insn = kFSOpSub;
- insn_sources = 2;
- } else if (!memcmp(s, "mul", 3)) {
- insn = kFSOpMul;
- insn_sources = 2;
- } else if (!memcmp(s, "mad", 3)) {
- insn = kFSOpMad;
- insn_sources = 3;
- } else if (!memcmp(s, "lrp", 3)) {
- insn = kFSOpLrp;
- insn_sources = 3;
- } else if (!memcmp(s, "dp3", 3)) {
- insn = kFSOpDp3;
- insn_sources = 2;
- } else if (!memcmp(s, "dp4", 3)) {
- insn = kFSOpDp4;
- insn_sources = 2;
- } else if (!memcmp(s, "dda", 3)) {
- insn = kFSOpDda;
- insn_dests = 3;
- insn_sources = 4;
- } else if (!memcmp(s, "mma", 3)) {
- insn = kFSOpMma;
- insn_dests = 3;
- insn_sources = 4;
- } else if (!memcmp(s, "def", 3)) {
- insn = kFSOpDef;
- insn_dests = 0;
- insn_sources = 4;
- }
- break;
-
- case 5:
- if (!memcmp(s, "final", 5)) {
- insn = kFSOpFinal;
- insn_dests = 0;
- insn_sources = 7;
- } else if (!memcmp(s, "texld", 5)) {
- insn = kFSOpTexld2Arg;
- insn_dests = 1;
- insn_sources = 1;
- } else if (!memcmp(s, "phase", 5)) {
- insn = kFSOpPhase;
- insn_dests = 0;
- insn_sources = 0;
- }
- break;
-
- case 6:
- if (!memcmp(s, "texcrd", 6)) {
- insn = kFSOpTexcrd;
- insn_dests = 1;
- insn_sources = 1;
- }
- break;
- }
-
- if (!insn)
- mLexer.ThrowError("Unknown fragment shader instruction '%.*s'", insnlen, s);
-
- // parse modifiers
- int modifiers = 0;
- while(insnlen < toklen) {
- if (s[insnlen] != '_')
- mLexer.ThrowError("Syntax error in fragment shader instruction");
-
- ++insnlen;
-
- const char *modbase = s + insnlen;
- int modstart = insnlen;
-
- while(insnlen < toklen && s[insnlen] != '_')
- ++insnlen;
-
- int modlen = insnlen - modstart;
- int modbit = 0;
- switch(modlen) {
- case 2:
- if (!memcmp(modbase, "x2", 2))
- modbit = kInsnModX2;
- else if (!memcmp(modbase, "x4", 2))
- modbit = kInsnModX4;
- else if (!memcmp(modbase, "x8", 2))
- modbit = kInsnModX8;
- else if (!memcmp(modbase, "d2", 2))
- modbit = kInsnModD2;
- else if (!memcmp(modbase, "d4", 2))
- modbit = kInsnModD4;
- else if (!memcmp(modbase, "d8", 2))
- modbit = kInsnModD8;
- break;
-
- case 3:
- if (!memcmp(modbase, "bx2", 3))
- modbit = kInsnModBX2;
- else if (!memcmp(modbase, "sat", 3))
- modbit = kInsnModSat;
- break;
-
- case 4:
- if (!memcmp(modbase, "bias", 4))
- modbit = kInsnModBias;
- break;
- }
-
- if (!modbit)
- mLexer.ThrowError("Invalid instruction modifier '%.*s'", modlen, modbase);
-
- modifiers |= modbit;
- }
-
- // def is special
- if (insn == kFSOpDef) {
- if (modifiers)
- mLexer.ThrowError("'def' instruction cannot have modifiers");
-
- if (coissue)
- mLexer.ThrowError("'def' instruction cannot be co-issued");
-
- GLCDestArg arg = ParseFragmentShaderDestination();
-
- if (arg.mWriteMask != 15 || (arg.mReg & kRegTypeMask) != kRegC0)
- mLexer.ThrowError("'def' must be used with a full constant register");
-
- int index = arg.mReg - kRegC0;
-
- if (shader.mUsedConstants & (1 << index))
- mLexer.ThrowError("Constant 'c%d' already defined", index);
-
- shader.mUsedConstants |= (1 << index);
-
- for(int i=0; i<4; ++i) {
- Expect(',');
- shader.mConstants[arg.mReg - kRegC0][i] = (float)ParseConstantDoubleExpression();
- }
- } else {
- GLCFragmentOp op={0};
-
- op.mInsn = insn;
- op.mModifiers = modifiers;
- op.mbCoIssue = coissue;
- coissue = false;
-
- // parse destinations
- for(int i=0; i<insn_dests; ++i) {
- if (i)
- Expect(',');
-
- op.mDstArgs[i] = ParseFragmentShaderDestination();
- if ((op.mDstArgs[i].mReg & kRegTypeMask) == kRegC0)
- mLexer.ThrowError("Constant registers cannot be used as destinations");
- }
-
- // parse sources
- for(int i=0; i<insn_sources; ++i) {
- if (i || insn_dests)
- Expect(',');
- op.mSrcArgs[i] = ParseFragmentShaderArgument();
- }
-
- op.mLocation = loc;
-
- Expect('\n');
-
- shader.mOps.push_back(op);
- }
- continue;
- }
-
- Huh("fragment shader instruction", tok);
- }
-
- mLexer.EnableNewlines(false);
- }
-
- GLCDestArg GLCCompiler::ParseFragmentShaderDestination() {
- GLCDestArg arg;
-
- int tok = mLexer.Token();
-
- if (tok != kTokenIdent)
- Huh("fragment shader register", tok);
-
- // parse out base register
- const char *s = mLexer.GetToken();
- int namelen = mLexer.GetTokenLen();
-
- if (memchr(s, '_', namelen))
- mLexer.ThrowError("Destination register cannot have modifier");
-
- arg.mReg = 0;
- if (namelen == 7 && !memcmp(s, "discard", 7)) {
- arg.mReg = kRegDiscard;
- } else {
- unsigned index = 0;
-
- for(int i=1; i<namelen; ++i) {
- if (!isdigit((unsigned char)s[i]))
- goto invalid_register;
-
- index = (index * 10) + (s[i] - '0');
- }
-
- if (s[0] == 't') {
- if (index >= 6)
- mLexer.ThrowError("Invalid texture register '%.*s'", namelen, s);
- arg.mReg = kRegT0 + index;
- } else if (s[0] == 'r') {
- if (index >= 6)
- mLexer.ThrowError("Invalid spare register '%.*s'", namelen, s);
- arg.mReg = kRegR0 + index;
- } else if (s[0] == 'c') {
- if (index >= 16)
- mLexer.ThrowError("Invalid constant register '%.*s'", namelen, s);
- arg.mReg = kRegC0 + index;
- } else if (s[0] == 'v') {
- if (index >= 2)
- mLexer.ThrowError("Invalid vertex interpolator register '%.*s'", namelen, s);
- arg.mReg = kRegV0 + index;
- }
- }
-
- if (!arg.mReg) {
- invalid_register:
- mLexer.ThrowError("Unknown fragment shader destination register '%.*s'", namelen, s);
- }
-
- // parse out write mask
- tok = mLexer.Token();
-
- arg.mWriteMask = 15;
- if (tok == '.') {
- tok = mLexer.Token();
-
- if (tok != kTokenIdent)
- Huh("register write mask", tok);
-
- const char *s = mLexer.GetToken();
- int len = mLexer.GetTokenLen();
-
- arg.mWriteMask = 0;
- for(int i=0; i<len; ++i) {
- int bit;
-
- switch(s[i]) {
- case 'r':
- bit = 1;
- break;
- case 'g':
- bit = 2;
- break;
- case 'b':
- bit = 4;
- break;
- case 'a':
- bit = 8;
- break;
-
- default:
- invalid_write_mask:
- mLexer.ThrowError("Invalid destination write mask");
- }
-
- if ((arg.mWriteMask & bit) || bit < arg.mWriteMask)
- goto invalid_write_mask;
-
- arg.mWriteMask |= bit;
- }
- } else {
- mLexer.Push(tok);
- }
-
- return arg;
- }
-
- GLCSourceArg GLCCompiler::ParseFragmentShaderArgument() {
- GLCSourceArg arg;
- int tok = mLexer.Token();
-
- // parse out complement and negation modifiers
- arg.mMods = 0;
- for(;;) {
- if (tok == '-')
- arg.mMods ^= kRegModNegate;
- else if (tok == kTokenInteger) {
- if (mLexer.GetInteger() != 1)
- mLexer.ThrowError("Invalid register modifier");
-
- Expect('-');
-
- arg.mMods |= kRegModComplement;
- } else
- break;
-
- tok = mLexer.Token();
- }
-
- if (tok != kTokenIdent)
- Huh("fragment shader register", tok);
-
- // parse out base register
- const char *s = mLexer.GetToken();
- int toklen = mLexer.GetTokenLen();
- int namelen = 0;
-
- // parse instruction itself
- while(namelen < toklen && s[namelen] != '_')
- ++namelen;
-
- arg.mReg = 0;
- if (namelen == 4 && !memcmp(s, "zero", 4)) {
- arg.mReg = kRegZero;
- } else if (namelen == 7 && !memcmp(s, "discard", 7)) {
- mLexer.ThrowError("'discard' cannot be used in a source argument");
- } else {
- unsigned index = 0;
-
- for(int i=1; i<namelen; ++i) {
- if (!isdigit((unsigned char)s[i]))
- goto invalid_register;
-
- index = (index * 10) + (s[i] - '0');
- }
-
- if (s[0] == 't') {
- if (index >= 6)
- mLexer.ThrowError("Invalid texture register '%.*s'", namelen, s);
- arg.mReg = kRegT0 + index;
- } else if (s[0] == 'r') {
- if (index >= 6)
- mLexer.ThrowError("Invalid spare register '%.*s'", namelen, s);
- arg.mReg = kRegR0 + index;
- } else if (s[0] == 'c') {
- if (index >= 16)
- mLexer.ThrowError("Invalid constant register '%.*s'", namelen, s);
- arg.mReg = kRegC0 + index;
- } else if (s[0] == 'v') {
- if (index >= 2)
- mLexer.ThrowError("Invalid vertex interpolator register '%.*s'", namelen, s);
- arg.mReg = kRegV0 + index;
- }
- }
-
- if (!arg.mReg) {
- invalid_register:
- mLexer.ThrowError("Unknown fragment shader register '%.*s'", namelen, s);
- }
-
- // parse modifiers
- while(namelen < toklen) {
- if (s[namelen] != '_')
- mLexer.ThrowError("Syntax error in fragment shader register");
-
- ++namelen;
-
- const char *modbase = s + namelen;
- int modstart = namelen;
-
- while(namelen < toklen && s[namelen] != '_')
- ++namelen;
-
- int modlen = namelen - modstart;
- int modbit = 0;
- switch(modlen) {
- case 2:
- if (!memcmp(modbase, "x2", 2))
- modbit = kRegModX2;
- break;
- case 3:
- if (!memcmp(modbase, "bx2", 3))
- modbit = kRegModBias | kRegModX2;
- else if (!memcmp(modbase, "sat", 3))
- modbit = kRegModSaturate;
- break;
-
- case 4:
- if (!memcmp(modbase, "bias", 4))
- modbit = kRegModBias;
- break;
- }
-
- if (!modbit)
- mLexer.ThrowError("Invalid register modifier '%.*s'", modlen, modbase);
-
- arg.mMods |= modbit;
- }
-
- tok = mLexer.Token();
-
- arg.mSwizzle = kSwizzleNone;
- arg.mSize = 4;
- if (tok == '.') {
- tok = mLexer.Token();
-
- if (tok != kTokenIdent)
- Huh("register swizzle", tok);
-
- const char *s = mLexer.GetToken();
- int len = mLexer.GetTokenLen();
-
- arg.mSwizzle = 0;
- arg.mSize = 0;
- for(int i=0; i<len; ++i) {
- int component;
-
- switch(s[i]) {
- case 'r':
- component = 0;
- break;
- case 'g':
- component = 1;
- break;
- case 'b':
- component = 2;
- break;
- case 'a':
- component = 3;
- break;
-
- default:
- invalid_swizzle:
- mLexer.ThrowError("Invalid register swizzle");
- }
-
- arg.mSwizzle += component << (2*i);
- ++arg.mSize;
- if (arg.mSize >= 4)
- goto invalid_swizzle;
- }
-
- // .a/r/g/b -> .aaaa/rrrr/gggg/bbbb
- if (arg.mSize == 1) {
- arg.mSize = 4;
- arg.mSwizzle *= 0x55;
- }
- } else {
- mLexer.Push(tok);
- }
-
- return arg;
- }
-
- double GLCCompiler::ParseConstantDoubleExpression() {
- vdfastvector<double> mValueStack;
- vdfastvector<int> mOpStack;
- int parensValue = 0;
- bool needValue = true;
-
- for(;;) {
- int tok = mLexer.Token();
-
- if (needValue) {
- if (tok == '(')
- parensValue += 0x1000000;
- else if (tok == '-')
- mOpStack.push_back(parensValue + 'N' + 0x60000);
- else if (tok == kTokenInteger) {
- mValueStack.push_back(mLexer.GetInteger());
- needValue = false;
- } else if (tok == kTokenDouble) {
- mValueStack.push_back(mLexer.GetDouble());
- needValue = false;
- } else if (tok != '+')
- Huh("numeric value", tok);
- } else {
- int opValue;
-
- if (tok == ')' && parensValue) {
- parensValue -= 0x1000000;
- continue;
- }
-
- if (tok == '+')
- opValue = parensValue + tok + 0x10000;
- else if (tok == '-')
- opValue = parensValue + tok + 0x10000;
- else if (tok == '*')
- opValue = parensValue + tok + 0x20000;
- else if (tok == '/')
- opValue = parensValue + tok + 0x20000;
- else if (tok == ';' || tok == ',' || tok == ')' || tok == '\n') {
- if (parensValue)
- mLexer.ThrowError("Unmatched ')'");
-
- mLexer.Push(tok);
- opValue = 0;
- } else
- Huh("expression operator", tok);
-
- while(!mOpStack.empty() && (mOpStack.back() & 0xffff0000) >= (opValue & 0xffff0000)) {
- int op = mOpStack.back() & 0xffff;
- mOpStack.pop_back();
-
- double y;
- switch(op) {
- case '+':
- y = mValueStack.back();
- mValueStack.pop_back();
- mValueStack.back() += y;
- break;
- case '-':
- y = mValueStack.back();
- mValueStack.pop_back();
- mValueStack.back() -= y;
- break;
- case '*':
- y = mValueStack.back();
- mValueStack.pop_back();
- mValueStack.back() *= y;
- break;
- case '/':
- y = mValueStack.back();
- if (y == 0.0f)
- mLexer.ThrowError("Division by zero");
- mValueStack.pop_back();
- mValueStack.back() /= y;
- break;
- case 'N':
- mValueStack.back() = -mValueStack.back();
- break;
- }
- }
-
- mOpStack.push_back(opValue);
-
- if (!opValue)
- break;
-
- needValue = true;
- }
- }
-
- VDASSERT(mValueStack.size() == 1);
- return mValueStack.back();
- }
-
- void GLCCompiler::Expect(int c) {
- int tok = mLexer.Token();
-
- if (c != tok)
- mLexer.ThrowError("Expected '%s', found '%s'", TokenName(c).c_str(), TokenName(tok).c_str());
- }
-
- void GLCCompiler::Huh(const char *expected, int found) {
- mLexer.ThrowError("Expected %s, found '%s'", expected, TokenName(found).c_str());
- }
-
- std::string GLCCompiler::TokenName(int tok) {
- switch(tok) {
- case kTokenIdent:
- return "identifier";
-
- case kTokenInteger:
- return "integer";
-
- case kTokenDouble:
- return "real";
-
- case kTokenForeignCode:
- return "foreign code";
-
- case '\n':
- return "end-of-line";
-
- default:
- {
- char buf[2]= { (char)tok, 0 };
- return buf;
- }
- break;
- }
- }
-
- ///////////////////////////////////////////////////////////////////////////////
- void tool_glc(const vdfastvector<const char *>& args, const vdfastvector<const char *>& switches) {
- if (args.size() < 2) {
- printf("usage: glc <binary file> <.cpp output file>\n");
- exit(5);
- }
-
- printf("Asuka: Compiling effect file (OpenGL): %s -> %s.\n", args[0], args[1]);
-
- FILE *f = fopen(args[0], "rb");
- if (!f)
- fail(" couldn't open: %s\n", args[0]);
- fseek(f, 0, SEEK_END);
- size_t l = ftell(f);
- vdfastvector<char> buf(l);
- fseek(f, 0, SEEK_SET);
- if (!buf.empty())
- fread(&buf[0], l, 1, f);
- fclose(f);
-
- f = fopen(args[1], "w");
- if (!f)
- fail(" couldn't open: %s\n", args[1]);
-
- fprintf(f, "// Automatically generated by Asuka from \"%s.\" DO NOT EDIT!\n\n", VDFileSplitPath(args[0]));
-
- GLCCompiler glc;
-
- glc.Compile(args[0], buf.data(), buf.size(), f);
-
- fclose(f);
-
- printf("Asuka: Compilation was successful.\n");
- }
-